/*!
 * =====================================================
 * ZJR v0.0.1
 * =====================================================
 */

/**
 * @author 文件上传插件
 * @param {DOM|string} opations.container DOM主容器
 * @param {string} opations.accept MIME 类型别名 例：'png,jpg,gif'，
 * @param {string} opations.mime MIME 不适用内置的mime配置，精准传值 MIME 参考手册： http://www.w3school.com.cn/media/media_mimeref.asp
 * @param {number} opations.count 文件数量限制
 * @param {number} opations.size 文件大小限制
 * @param {boolean} opations.repeat 容许文件重复 默认 false
 * @param {boolean} opations.multiple 多文件上传 默认 false
 * @param {function} opations.callBack 文件change选取回调 回调参数：文件list、客户端url地址(一般用于图片预览)，item 容器、更换文件的index：changeIndex（用于判断回调是在更换文件）
 * @param {boolean} opations.showFileName 显示文件名 默认 true
 * @param {boolean} opations.showProgress 显示进度条 默认 false ，若为true：请在callBack回调调用 this.progress(100); 表示上传完成
 * @param {function} opations.msgCallBack 提示消息钩子函数 参数类型：msg|warn|err ，该函数方便使用自己喜欢的弹窗提示消息
 */

function UploadFile(opations) {
	let o = { init: init };
	//可配置参数
	const config = {
		fileTypes: {
			//压缩
			zip: { mime: 'application/zip', alias: 'zip', suffix: '.zip', className: 'zip' },
			//image
			bmp: { mime: 'image/bmp', alias: 'bmp', suffix: '.bmp', className: 'img' },
			cod: { mime: 'image/cis-cod', alias: 'cod', suffix: '.cod', className: 'img' },
			gif: { mime: 'image/gif', alias: 'gif', suffix: '.gif', className: 'img' },
			ief: { mime: 'image/ief', alias: 'ief', suffix: '.ief', className: 'img' },
			jpe: { mime: 'image/jpeg', alias: 'jpe', suffix: '.jpe', className: 'img' },
			jpeg: { mime: 'image/jpeg', alias: 'jpeg', suffix: '.jpeg', className: 'img' },
			jpg: { mime: 'image/jpeg', alias: 'jpg', suffix: '.jpg', className: 'img' },
			jfif: { mime: 'image/pipeg', alias: 'jfif', suffix: '.jfif', className: 'img' },
			svg: { mime: 'image/svg+xml', alias: 'svg', suffix: '.svg', className: 'img' },
			tif: { mime: 'image/tiff', alias: 'tif', suffix: '.tif', className: 'img' },
			tiff: { mime: 'image/tiff', alias: 'tiff', suffix: '.tiff', className: 'img' },
			ras: { mime: 'image/x-cmu-raster', alias: 'ras', suffix: '.ras', className: 'img' },
			cmx: { mime: 'image/x-cmx', alias: 'cmx', suffix: '.cmx', className: 'img' },
			ico: { mime: 'image/x-icon', alias: 'ico', suffix: '.ico', className: 'img' },
			pnm: { mime: 'image/x-portable-anymap', alias: 'pnm', suffix: '.pnm', className: 'img' },
			pbm: { mime: 'image/x-portable-bitmap', alias: 'pbm', suffix: '.pbm', className: 'img' },
			pgm: { mime: 'image/x-portable-graymap', alias: 'pgm', suffix: '.pgm', className: 'img' },
			ppm: { mime: 'image/x-portable-pixmap', alias: 'ppm', suffix: '.ppm', className: 'img' },
			rgb: { mime: 'image/x-rgb', alias: 'rgb', suffix: '.rgb', className: 'img' },
			xbm: { mime: 'image/x-xbitmap', alias: 'xbm', suffix: '.xbm', className: 'img' },
			xpm: { mime: 'image/x-xpixmap', alias: 'xpm', suffix: '.xpm', className: 'img' },
			xwd: { mime: 'image/x-xwindowdump', alias: 'xwd', suffix: '.xwd', className: 'img' },
			png: { mime: 'image/png', alias: 'png', suffix: '.png', className: 'img' },
			//media
			mp4: { mime: 'video/mp4', alias: 'mp4', suffix: '.mp4', className: 'mp4' },
			//word
			doc: { mime: 'application/msword', alias: 'doc,dot', suffix: '.doc', className: 'word' },
			dot: { mime: 'application/msword', alias: 'doc', suffix: '.dot', className: 'word' },
			docx: { mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', alias: 'docx', suffix: '.docx', className: 'word' },
			dotx: { mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', alias: 'dotx', suffix: '.dotx', className: 'word' },
			//excel
			xls: { mime: 'application/vnd.ms-excel', alias: 'xls', suffix: '.xls,', className: 'excel' },
			xlt: { mime: 'application/vnd.ms-excel', alias: 'xlt', suffix: '.xlt,', className: 'excel' },
			xlsx: { mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', alias: 'xlsx', suffix: '.xlsx,', className: 'excel' },
			xlsm: { mime: 'application/vnd.ms-excel.sheet.macroenabled.12', alias: 'xlsm', suffix: '.xlsm,', className: 'excel' },
			//ppt
			ppt: { mime: 'application/vnd.ms-powerpoint', alias: 'ppt', suffix: '.ppt', className: 'ppt' },
			pptx: { mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', alias: 'pptx', suffix: '.pptx', className: 'ppt' },

			pdf: { mime: 'application/pdf', alias: 'pdf', suffix: '.pdf', className: 'pdf' },
			txt: { mime: 'text/plain', alias: 'txt', suffix: '.txt', className: 'txt' },
			//安装程序
			apk: { mime: 'application/vnd.android.package-archive', alias: 'apk', suffix: '.pdf', className: 'apk' },
			ipa: { mime: null, alias: 'ipa', suffix: '.ipa', className: 'ipa' },
			//其他
			undefined: { mime: undefined, alias: undefined, suffix: undefined, className: 'undfined' },
		},
		constFileType: {// image/*
			image: 'bmp,cod,gif,ief,jpe,jpeg,jpg,jfif,svg,tif,tiff,ras,cmx,ico,pnm,pbm,pgm,ppm,rgb,xbm,xwd,png'
		},
		backgroundImageFiles: 'bmp,gif,jpe,jpeg,jpg,svg,ico,pbm,png',//自身可用于做背景图片的文件类型
		container: {
			className: 'upload-file-cont' //修改请对应update.file.css
		},
	}
	//初始化
	function init() {
		//初始化数据
		let fileBox; //文件容器
		let alias = '';
		let accepts = [];
		let isAccept = true;

		if (typeof opations.container == 'string') { //获取主容器
			fileBox = document.querySelectorAll(opations.container)[0] || null;
		} else {
			fileBox = opations.container.nodeType == 1 ? opations.container : null;
		}
		if (!fileBox) {
			console.error('container 不是dom对象或选择器格式有误');
			return false;
		}
		fileBox.innerHTML = '';
		fileBox.classList.add(config.container.className);

		if (opations.accept == 'image/*') {// 值为image/* 会导致加载缓慢
			opations.accept = config.constFileType.image;
		}
		opations.accept = opations.accept.replace(/image\/\*/g, config.constFileType.image);
		alias = opations.accept.split(/，| |;|\/|-|:|,/);
		for (const index in alias) {
			const key = alias[index];
			if (!key) continue;
			if (config.fileTypes[key] && config.fileTypes[key].mime) {
				accepts.push(config.fileTypes[key].mime);
			} else {
				isAccept = false;
				break;
			}
		}
		opations.accepts = isAccept ? accepts : null;
		o = {
			init: init, //重新初始化上传插件
			rest: restUpload, //重置上传插件
			files: [], //文件 file 对象
			filesNotes: [], //{name id key-value} 文件记录
			msgCallBack: opations.msgCallBack || function () { }, //消息回调
			msg: spreadMessage, //消息广播
			disabled: uploadDisabled, //禁用上传操作
			active: uploadDActive, //启用上传操作
			box: { //dom 容器
				fileBox: fileBox, //主容器
				progress: null, //进图条
				items: [], // 文件DOM 队列
			},
			/* 上传文件参数 */
			index: 0, //文件index
			accept: opations.accept,//文件mime别名
			accepts: opations.mime || opations.accepts, //文件mime格式
			showFileName: opations.showFileName || true, //文件名称显示开关
			callBack: opations.callBack || function () { }, //上传文件change 回调
			showProgress: opations.showProgress || false, //进度条显示开关
			progress: progress, //上传进度
			progressTimeout: null, //进度Timeout
			size: opations.size > 0 ? opations.size : Infinity, //文件大小
			count: opations.count > 0 ? opations.count : Infinity, //文件数量
			repeat: opations.repeat || false, //文件重复
			multiple: opations.multiple || false, //多文件上传
		}
		//初始化布局
		newPlaceholder();
	}
	//生成占位上传按钮
	function newPlaceholder(file, targetUrl) {
		let fileInputArray = getFileInputArray();
		if (o.files.length >= o.count ||
			!file &&
			fileInputArray &&
			fileInputArray.length > 0 &&
			fileInputArray[fileInputArray.length - 1].parentNode.attributes['data-type']) {
			return;
		};
		//img-item
		let placeholder = document.createElement('div');
		placeholder.setAttribute('class', 'image-item');
		placeholder.setAttribute('data-type', 'upload-input');
		//上传按钮的背景
		let upImg = document.createElement("div");
		upImg.setAttribute('class', 'image-up')

		//描述-文件名
		let fileNameElem = document.createElement('div');
		fileNameElem.setAttribute('class', o.showFileName ? 'file-name-fot' : 'file-name-fot hide');
		//删除按钮
		let closeButton = document.createElement('div');
		closeButton.setAttribute('class', 'image-close');
		closeButton.setAttribute('data-key', 'img-' + o.index);

		//上传 input
		let fileInput = document.createElement('input');
		fileInput.setAttribute('type', 'file');
		fileInput.setAttribute('class', 'file');
		if (o.accepts) fileInput.setAttribute('accept', o.accepts);
		if (o.multiple) fileInput.setAttribute('multiple', 'multiple');
		fileInput.setAttribute('name', 'image-' + o.index);
		fileInput.setAttribute('data-value', 'image-' + o.index);
		fileInput.setAttribute('data-key', 'image-' + o.index);

		if (file) {
			placeholder.removeAttribute('data-type');
			fileInput.removeAttribute('multiple');
			fileNameElem.innerText = file.name;
			let bgObj = getDisplayFileImg(file.name, targetUrl)
			placeholder.classList.add(bgObj.className);
			if(bgObj.bgUrl ) placeholder.style.backgroundImage = 'url('+ bgObj.bgUrl +')';
			o.index++;
		}

		o.box.fileBox.appendChild(placeholder);
		if (!targetUrl) placeholder.appendChild(upImg);
		placeholder.appendChild(fileNameElem);
		placeholder.appendChild(closeButton);
		placeholder.appendChild(fileInput);

		//删除文件
		closeButton.addEventListener('click', function (event) {
			if (placeholder.querySelectorAll('.file')[0].classList.contains('hide')) {
				return false;
			}
			setTimeout(function () {
				for (let i = 0; i < o.filesNotes.length; i++) {
					if (o.filesNotes[i].key == closeButton.getAttribute('data-key')) {
						removeFile(i);
						newPlaceholder();
						break;
					}
				}
			}, 0);
			o.box.fileBox.removeChild(placeholder);
			return false;
		}, false);
		//选择文件
		fileInput.addEventListener('change', function (event) {
			let self = this;
			let files = self.files;
			let changeIndex = -1;
			//文件校验
			let checkResult = checkFile(files);
			if (!checkResult.status) {
				self.value = '';
				o.msg(checkResult.message);
				return false;
			}
			//更换文件逻辑
			if (this.parentNode.getAttribute('data-type') != 'upload-input') {
				let resFile = files[0];
				let targetUrl = getFileURL(resFile);
				for (let i = 0; i < o.filesNotes.length; i++) {
					if (o.filesNotes[i].name == self.getAttribute('name')) {
						updateFile(resFile, targetUrl, i);
						changeIndex = i;
						break;
					}
				}
				//展示文件类型对应icon
				displayFileImg(resFile.name, resFile.type, placeholder, targetUrl);
			} else {//添加文件逻辑
				placeholder.remove();
				for (let i = 0; i < files.length; i++) {
					const file = files[i];
					let targetUrl = getFileURL(file);
					//添加文件到队列
					addFile(file, targetUrl);
					newPlaceholder(file, targetUrl);
					//展示文件类型对应icon
					displayFileImg(file.name, file.type, placeholder, targetUrl);
				}
			}
			//更新图片展示布局
			updateLayout(true);
			//文件change选取回调
			o.callBack(files, targetUrl, placeholder, changeIndex);

		}, false);
		//上传中提示
		placeholder.addEventListener('click', function (event) {
			if (this.querySelectorAll('.file')[0].classList.contains('hide')) {
				o.msg(message('正在上传，请稍后', 'warn', 'upload-process', 'process'));
			}
		})
		//添加items
		o.box.items.push(placeholder);
	};
	//上传进度
	function progress(val, actual) {
		o.showProgress = true; //开启进度条
		uploadDisabled(); //禁止文件上传操作
		val = val || 0;
		if (val > 100) val = 100;
		let _progressElem = o.box.progress;
		if (!_progressElem) {
			_progressElem = document.createElement('div');
			_progressElem.classList.add('upload-file-progress');
			o.box.progress = _progressElem;
			o.box.fileBox.appendChild(_progressElem);
		}
		_progressElem.classList.add('active');
		let _proVal = parseInt(o.box.progress.getAttribute('val')) || 0;
		if (actual && val < 100) { //实时进度显示
			_proVal = val;
			if (o.progressTimeout) clearTimeout(o.progressTimeout);//清除自动计算
		} else if (val < 100) { //计算模拟进度
			_proVal += parseInt(Math.random() * 7);
			_proVal = _proVal >= 98 ? 98 : _proVal;
			o.progressTimeout = setTimeout(progress, 1 * _proVal * _proVal / 20);
		} else { //100进度与初始化处理
			_proVal = 100;
			if (o.progressTimeout) clearTimeout(o.progressTimeout);//清除自动计算
			uploadDActive(); //容许上传操作
			setTimeout(function () {//初始化进度条
				o.box.progress.style.width = 0 + '%';
				_progressElem.classList.remove('active');
			}, 100);
		}
		//更新进度
		o.box.progress.style.width = _proVal + '%';
		o.box.progress.setAttribute('val', _proVal);
		return _proVal;
	}
	//禁止上传操作
	function uploadDisabled() {
		let fileInputArray = getFileInputArray();
		for (let i = 0; i < fileInputArray.length; i++) {
			fileInputArray[i].classList.add('hide');
		}
	}
	//开启上传操作
	function uploadDActive() {
		let fileInputArray = getFileInputArray();
		for (let i = 0; i < fileInputArray.length; i++) {
			fileInputArray[i].classList.remove('hide');
		}
	}
	//添加记录
	function addFile(file, path) {
		o.files.push(file);
		o.filesNotes.push({
			name: "image-" + o.index,
			path: path,
			key: "img-" + o.index
		});
	};
	//更新files
	function updateFile(file, path, index) {
		o.filesNotes[index].path = path;
		o.files[index] = file;
	}
	//移除记录
	function removeFile(index) {
		o.filesNotes.splice(index, 1);
		o.files.splice(index, 1);
	}
	//验证文件，文件校验
	function checkFile(files) {
		let reslut = { status: true };
		for (let i = 0; i < files.length; i++) {
			let file = files[i];
			const nameArr = file.name.split('.');
			const alias = nameArr[nameArr.length - 1].toLowerCase() || null;
			//重复文件
			for (let i = 0; i < o.files.length; i++) {
				if (o.files[i].name == file.name &&
					o.files[i].size == file.size &&
					o.files[i].lastModified == file.lastModified) {
					reslut.status = false;
					reslut.message = message('已存在' + o.files[i].name + '文件', 'warn', 'repeat', 'select');
				}
			}
			//简单判断后缀
			if (!alias || o.accept.indexOf(alias) == -1) {
				reslut.status = false;
				reslut.message = message('请上传' + o.accept + '类型的文件', 'error', 'fileType', 'select');
			} else if (file.size > o.size) {//验证文件大小
				reslut.status = false;
				reslut.message = message('请上传小于' + fileSizeUnit(o.size) + '的文件', 'error', 'size', 'select');
			} else if (files.length + o.files.length > o.count) {//验证文件数量
				reslut.status = false;
				reslut.message = message('最多上传' + o.count + '个文件', 'error', 'count', 'select');
			}
		}
		return reslut;
	}
	//获取 fileInputs
	function getFileInputArray() {
		let fileInputArray = [];
		for (let i = 0; i < o.box.items.length; i++) {
			fileInputArray.push(o.box.items[i].querySelectorAll('input[type=file]')[0]);
		}
		return fileInputArray;
	}
	//更新页面UI展示
	function updateLayout(newUpdateBox) {
		//更新创建上传占位块
		if (newUpdateBox) newPlaceholder();
		//展示进度条
		if (o.showProgress) {
			o.progress(0);
		};
	}
	//展示文件对应图片
	function displayFileImg(fileName, fileType, placeholder, targetUrl) {
		let bgObj = getDisplayFileImg(fileName, targetUrl);
		placeholder.classList.add(bgObj.className);
		if(bgObj.bgUrl ) placeholder.style.backgroundImage = 'url('+ bgObj.bgUrl +')';
	}
	function getDisplayFileImg(fileName, targetUrl) {
		const nameArr = fileName.split('.');
		let fileAlias = nameArr[nameArr.length - 1].toLowerCase();
		let bgUrl = config.backgroundImageFiles.indexOf(fileAlias) >= 0 ? targetUrl : '';
		let className = config.fileTypes[fileAlias] ? config.fileTypes[fileAlias].className : config.fileTypes.undefined.className;
		return {
			bgUrl: bgUrl,
			className: className
		};
	}
	//生成一个指向file或blod对象的URL
	function getFileURL(file) {
		let url = null;
		if (window.createObjcectURL != undefined) {
			url = window.createOjcectURL(file);
		} else if (window.URL != undefined) {
			url = window.URL.createObjectURL(file);
		} else if (window.webkitURL != undefined) {
			url = window.webkitURL.createObjectURL(file);
		}
		return url;
	}
	//生成消息对象
	function message(msg, type, cause, status) {
		return { msg: msg, type: type, cause: cause, status: status };
	}
	//消息广播
	function spreadMessage(msgObj) {
		//初始化提示
		let msgCallBack = o.msgCallBack || function () {};
		if (!o.msgCallBack) console.warn('建议传入msg消息提醒钩子函数，处理文件上传过程消息提醒。');
		if (msgObj.type == 'warn') console.warn(msgObj.msg);
		if (msgObj.type == 'error') console.error(msgObj.msg);
		msgCallBack(msgObj);
	}
	//重置
	function restUpload(callBack) {
		callBack = callBack || function () { };
		o.files = [];
		o.filesNotes = [];
		o.index = 0;
		o.box.items = [];
		o.box.fileBox.innerHTML = '';
		//初始化布局
		newPlaceholder();
		callBack(o);
	}
	//输出文件单位
	function fileSizeUnit(size) {
		const b = 1;
		const kb = b * 1024;
		const mb = kb * 1024;
		const gb = mb * 1024;
		const tb = gb * 1024;
		const pb = tb * 1024;
		const eb = pb * 1024;
		const zb = eb * 1024;
		const yb = zb * 1024;

		let unit = '';
		let sizeUnit = size;

		if (kb > size) {
			unit = 'B';
		} else if (mb > size) {
			unit = 'KB';
			sizeUnit = size / kb;
		} else if (gb > size) {
			unit = 'MB';
			sizeUnit = size / mb;
		} else if (tb > size) {
			unit = 'GB';
			sizeUnit = size / gb;
		} else if (pb > size) {
			unit = 'TB';
			sizeUnit = size / tb;
		} else if (eb > size) {
			unit = 'PB';
			sizeUnit = size / pb;
		} else if (zb > size) {
			unit = 'EB';
			sizeUnit = size / eb;
		} else if (yb > size) {
			unit = 'ZB';
			sizeUnit = size / zb;
		} else if (yb < size) {
			unit = 'YB';
			sizeUnit = size / yb;
		}

		return sizeUnit + unit;

	}
	o.init();
	return o;
}

/**
 * 消息回调 msg 返回参数字段说明 
 * @param {string} msg 文本
 * @param {string} type 类型 warn | error
 * @param {string} cause 原因 size | fileType 
 * @param {string} status 暂未使用
 */